/*******************************************************************************
* 
* Bot
* 
* 
******************************************************************************/
#include "autobot.h"
#include "logging.h"
#include "strings.h"
#include "file-helper.h"
#include "process.h"

#include <json/reader.h>
#include <json/value.h>

#include <algorithm>
#include <atlbase.h>
#include <mshtml.h>
#include <SHLGUID.h>

using namespace std;
using namespace noria::text;
using namespace noria::io;
using namespace noria::diagnostic;

namespace noria{ namespace bot
{
    Autobot::Autobot(void)
        : m_pwd(Process::getModulePath(L"athena"))
    {
    }


    Autobot::~Autobot(void)
    {
    }


    void Autobot::load(Json::Value& root)
    {
        Json::Reader reader;
        auto doc = FileHelper::readText(m_pwd + L"\\athena.js");
        if(!reader.parse(doc, root))
        {
            throw runtime_error(reader.getFormatedErrorMessages());
        }
    }

    void find(IUnknown* pUnk, IHTMLDocument2** ppDoc2)
    {
        HRESULT hr;

        CComQIPtr<IServiceProvider> provider(pUnk);
        if(!provider)
        {
            LOG_FATAL("Failure querying interface IServiceProvider");
            AtlThrow(E_NOINTERFACE);
        }

        CComQIPtr<IWebBrowser2> browser;
        hr = provider->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, reinterpret_cast<void **>(&browser));
        if (FAILED(hr))
        {
            LOG_FATAL("Failure querying service IWebBrowser2");
            AtlThrow(hr);
        }

        CComDispatchDriver disp;
        hr = browser->get_Document(&disp);
        if (FAILED(hr))
        {
            LOG_FATAL("Failure getting document IDispatch");
            AtlThrow(hr);
        }

        hr = disp->QueryInterface(IID_IHTMLDocument2, (void**)ppDoc2);
        if (FAILED(hr))
        {
            LOG_FATAL("Failure querying interface IHTMLDocument2");
            AtlThrow(hr);
        }
    }

    void eval(IHTMLWindow2* pWin2, const string& js)
    {
        CComVariant ret;
        auto code = Strings::a2w(js);
        HRESULT hr = pWin2->execScript(CComBSTR(code.c_str()), L"JavaScript", &ret);
        if(FAILED(hr))
            AtlThrow(hr);    
    }

    void Autobot::run(pointer unknown)
    {
        HRESULT hr;
        IUnknown* pUnk = static_cast<IUnknown*>(unknown);

        Json::Value athena;
        load(athena);

        CComQIPtr<IHTMLDocument2> spDoc2;
        find(pUnk, &spDoc2);

        CComBSTR url;
        hr = spDoc2->get_URL(&url);
        if(FAILED(hr))
            AtlThrow(hr);

        // get utf8 url
        auto utf8_url = Strings::w2a(url.Detach());

        auto& pages = athena["pages"];
        Json::ValueIterator current;
        int i = 0, page_size = pages.size();
        for(current = pages.begin(); current != pages.end() && i < page_size; current++, i++)
        {
            auto p = *current;

            // check if the url starts with the given page.url
            if(utf8_url.find(p["url"].asString()) == 0)
                break;
        }

        //auto current = find_if(pages.begin(), pages.end(), [&url](const Json::Value& p){
        //    // get utf8 url
        //    auto utf8_url = Strings::w2a(url.Detach());

        //    // check if the url starts with the given page.url
        //    return utf8_url.find(p["url"].asString()) == 0;
        //});

        if(current == pages.end() || i == page_size)
            throw runtime_error("Page was not defined.");

        // if found, get the bot.js
        auto js = FileHelper::readText(m_pwd + L"\\" + Strings::a2w((*current)["bot"].asString()));
        if(!js.length())
            throw runtime_error("Failed to load bot.");

        // get window
        CComQIPtr<IHTMLWindow2> spWin2;
        hr = spDoc2->get_parentWindow(&spWin2);
        if(FAILED(hr))
            AtlThrow(hr);

        // call eval(preloads)
        auto& preload = athena["preload"];
        for(auto it = preload.begin(); it != preload.end(); it++)
        {
            auto p = *it;

            auto req = FileHelper::readText(m_pwd + L"\\" + Strings::a2w(p.asString()));
            if(!req.length())
                throw runtime_error("Failed to load preloads.");

            eval(spWin2, req);
        }


        // call eval(bot)
        eval(spWin2, js);
    }
}}